﻿<?php
/**
 * This is the file to process page request.
 * 
 * @author	Justin Yoo
 * @version	0.0.1.0001
 * @package	KYR.Core
 * 
 */

//	Checks if this file is called within the framework; otherwise terminates.
if (!defined("IS_FRAMEWORK") || !IS_FRAMEWORK)	header("Location: http://".$_SERVER["HTTP_HOST"]."/error/404");

//	Imports relevant files.
require_once CORE_PATH."KYR.Core.BaseObject.php";
require_once CORE_PATH."KYR.Core.Database.php";
require_once EXCEPTION_PATH."KYR.Exceptions.DatabaseException.php";

/**
 * This is class Request that processes page request.
 *
 * Processes the page request.
 * 
 * @author	Justin Yoo
 * @package	KYR.Core
 * 
 */
class Request extends BaseObject
{
	private $_clientGets	= array();
	private $_clientPosts	= array();

	/**
	 * Initiates the Request class.
	 *
	 * @access	public
	 *
	 */
	public function __construct()
	{
		parent::__construct();

		//	Initialisation.
		$this->method		= null;
		$this->isAjax		= false;
		$this->user			= array();
		$this->uri			= null;
		$this->existUri		= false;
		$this->pageID		= null;
		$this->title		= null;
		$this->isAlias		= false;
		$this->aliasedUri	= null;
		$this->isSecure		= false;
		$this->variables	= array();
		$this->actionStep	= null;
		
		//	Parses request variables.
		$this->_Parse();
	}

	/**
	 * Gets the list of objects names.
	 *
	 * @access	protected
	 * @param	string	$type	Type of object to get names.
	 * @return	array			Returns the list of object names.
	 *
	 */
	protected function GetObjectNames_($type) {}

	/**
	 * Parses the page request.
	 *
	 * @access	private
	 *
	 */
	private function _Parse()
	{
		//	Gets the request method.
		$this->method		= strtolower(trim($_SERVER["REQUEST_METHOD"]));

		//	Checks if the request is AJAX or not.
		if (isset($_SERVER["HTTP_X_REQUESTED_WITH"]) && strtolower($_SERVER["HTTP_X_REQUESTED_WITH"]) == "xmlhttprequest")
			$this->isAjax	= true;

		//	Gets the remote user information
		$this->user			= $this->_GetUserInfo();

		//	Gets the GET variables.
		if ($this->method == "get")
		{
			if (isset($_GET["uri"]))
			{
				//	Adjusts the GET variables not to be harmful.
				foreach ($_GET as $key => $value)
					$_GET[$key] = $this->_CleanValue($value);

				//	Sets the URI.
				$this->uri		= trim($_GET["uri"]);

				//	Removes the trailing slash from the URI, if exists
				if (strlen($this->uri) > 1 && substr($this->uri, -1) == "/")
					$this->uri	= substr($this->uri, 0, strlen($this->uri) - 2);

				//	Sets the GET variables.
				$this->_clientGets	= $_GET;
				unset($this->_clientGets["uri"]);
			}
			else
			{
				//	Sets the URI.
				$this->uri		= "home";
			}
		}

		//	Gets the POST variables.
		if ($this->method == "post")
		{
			//	Adjusts the POST variables not to be harmful.
			foreach ($_POST as $key => $value)
				$_POST[$key] = $this->_CleanValue($value);

			//	Gets the POST variables.
			$this->_clientPosts		= $_POST;
		}
		
		//	Merges both GET values and POST values into one array.
		$this->_MergeRequests();
		
		//	Sets the actionStep using the GET/POST variables.
		$this->_SetActionStep();
	}

	/**
	 * Gets the client user information.
	 *
	 * @access	private
	 * @return	array		Returns the clinet user information.
	 *
	 */
	private function _GetUserInfo()
	{
		$user = array(
			"host"		=> null,
			"userAgent"	=> $_SERVER["HTTP_USER_AGENT"],
			"referrer"	=> null,
			"ip"		=> null
			);

		$this->_getRemoteAddress($user);
		$this->_GetClientIp($user);
		
		return $user;
	}
	
	/**
	 * Gets the client's remote address details.
	 *
	 * @access	private
	 * @param	array	$user	Array that contains user information.
	 *
	 */
	private function _GetRemoteAddress(&$user)
	{
		$direct	= null;
		if (isset($_SERVER["REMOTE_ADDR"]))
			$direct	= $_SERVER["REMOTE_ADDR"];
		elseif (isset($_ENV["REMOTE_ADDR"]))
			$direct	= $_ENV["REMOTE_ADDR"];
		else
			$direct	= getenv("REMOTE_ADDR");

		//	Gets the proxy information
		$proxyKeys	= array(
			"HTTP_CLIENT_IP",
			"HTTP_X_FORWARDED_FOR",
			"HTTP_X_FORWARDED",
			"HTTP_FORWARDED_FOR",
			"HTTP_FORWARDED",
			"HTTP_VIA",
			"HTTP_X_COMING_FROM",
			"HTTP_COMING_FROM"
			);

		$proxy	= null;
		$client	= null;
		foreach ($proxyKeys as $proxyKey)
		{
			if (isset($_SERVER[$proxyKey]))
			{
				$client	= $_SERVER["HTTP_X_FORWARDED_FOR"];
				break;
			}
			elseif (isset($_ENV["HTTP_X_FORWARDED_FOR"]))
			{
				$client	= $_ENV["HTTP_X_FORWARDED_FOR"];
				break;
			}
			else
			{
				$client	= getenv("HTTP_X_FORWARDED_FOR");
				break;
			}
		}


		if (empty($client))
			$client	= $direct;
		else
		{
			$isIp	= preg_match("|^([0-9]{1,3}\.){3,3}[0-9]{1,3}|", $client, $regs);
			$client = $isIp? $regs[0] : "";
			$proxy  = $direct;
		}

		$user["host"]		= $client;
		$user["referrer"]	= $proxy;
	}
	
	/**
	 * Gets the client's IP address details.
	 *
	 * @access	private
	 * @param	array	$user	Array that contains user information.
	 *
	 */
	private function _GetClientIp(&$user)
	{
		$ip			= null;

		//	Gets the remote IP address in various situation.
		if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
			$ip		= preg_replace("/(?:,.*)/", "", $_SERVER["HTTP_X_FORWARDED_FOR"]);
		else
			if (isset($_SERVER["HTTP_CLIENT_IP"]))
				$ip	= $_SERVER["HTTP_CLIENT_IP"];
			else
				$ip	= $_SERVER["REMOTE_ADDR"];

		if (isset($_SERVER["HTTP_CLIENTADDRESS"]))
			$ip		= preg_replace("/(?:,.*)/", "", $_SERVER["HTTP_CLIENTADDRESS"]);

		$user["ip"]	= trim($ip);
	}
	
	/**
	 * Cleans the GET/POST variables that contains potential harmness.
	 *
	 * @access	private
	 * @param	array	$input	Array that contains GET/POST variables.
	 * @return	array			Returns GET/POST variables removed potential harmness.
	 *
	 */
	private function _CleanValue($input)
	{
		if (is_array($input))
			foreach ($input as $key => $value)
				$input[$key] = $this->_CleanValue($value);
		else
		{
			$input = preg_replace("/[\r\t]+/",	"",										$input);
			$input = preg_replace("/\n{2,}/",	($this->method == "get" ? "" : "\n"),	$input);
			$input = preg_replace("/\s{2,}/",	($this->method == "get" ? "+" : " "),	$input);
			$input = htmlentities($input, ENT_QUOTES, "UTF-8");
			$input = trim($input);
		}
		return $input;
	}
	
	/**
	 * Merges bot GET and POST variables into one array.
	 *
	 * @access	private
	 *
	 */
	private function _MergeRequests()
	{
		$variables = array();
		//	Merges GET variables into $variables array.
		foreach ($this->_clientGets as $key => $value)
			$variables[$key] = $value;
		//	Merges POST variables into $variables array.
		foreach ($this->_clientPosts as $key => $value)
			$variables[$key] = $value;
		
		$this->variables = $variables;
	}

	/**
	 * Checks if the input URI exists or not.
	 *
	 * @access	private
	 *
	 */
	public function CheckUri()
	{
		if (strtolower(substr($this->uri, 0, strpos($this->uri, "/"))) == "error")
		{
			$this->existUri	= true;
			$this->isAlias	= false;
			$this->isSecure	= false;
			$this->title	= substr($this->uri, strpos($this->uri, "/") + 1);
		}
		else
		{
			$db		= null;
			$qry	= null;
			try
			{
				$db = new Database("MySQL");
				$qry = "CALL cms_CheckUri(";
				$qry .= "'".$db->Quote($this->uri)."'";
				$qry .= ")";
				$db->Query($qry);
				$row = $db->Fetch();
				//Debug::PrintScreen("pre", $row);

				if (isset($row) && count($row) > 0)
				{
					$this->existUri		= true;
					$this->pageID		= $row["pageID"];
					$this->title		= $row["title"];
					$this->isAlias		= $row["isAlias"] ? true : false;
					$this->aliasedUri	= $row["aliasedUri"];
					$this->isSecure		= $row["isSecure"] ? true : false;
				}
				else
				{
					$this->existUri		= false;
					$this->isAlias		= false;
					$this->isSecure		= false;
				}
			}
			catch (DatabaseException $ex)
			{
				$ex->query		= $qry;
				$this->existUri	= false;
				$this->isAlias	= false;
				$this->isSecure	= false;
				throw $ex;
			}
		}
	}
	
	/**
	 * Sets the actionStep of the page using the GET/POST variables.
	 *
	 * @access	private
	 *
	 */
	private function _SetActionStep()
	{
		if (!empty($this->variables["a"]))
			$this->actionStep = $this->variables["a"];
		else
			$this->actionStep = "view";
	}
}
?>
